Skip to content

Fix/#62 payment response#75

Merged
whc9999 merged 2 commits into
mainfrom
fix/#62-payment-response
May 22, 2026
Merged

Fix/#62 payment response#75
whc9999 merged 2 commits into
mainfrom
fix/#62-payment-response

Conversation

@whc9999
Copy link
Copy Markdown
Collaborator

@whc9999 whc9999 commented May 22, 2026

✨ 어떤 이유로 PR를 하셨나요?

  • feature 병합
  • 버그 수정(아래에 issue #를 남겨주세요)
  • 코드 개선
  • 코드 수정
  • 배포
  • 기타(아래에 자세한 내용 기입해주세요)

📋 세부 내용 - 왜 해당 PR이 필요한지 작업 내용을 자세하게 설명해주세요

📸 작업 화면 스크린샷

⚠️ PR하기 전에 확인해주세요

  • 로컬테스트를 진행하셨나요?
  • 머지할 브랜치를 확인하셨나요?
  • 관련 label을 선택하셨나요?

🚨 관련 이슈 번호 [#62]

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced payment response handling to gracefully manage responses containing additional or unexpected data fields.
  • Chores

    • Implemented system-wide activity tracking infrastructure for analysis operations, question management, job posting activities, and application processes to improve operational visibility.

Review Change Stack

whc9999 added 2 commits May 23, 2026 05:43
- AuditLog 엔티티와 Repository, Service 추가
- 요청 IP와 User-Agent 자동 수집 처리
- 공고 생성/수정, 모의 서류 지원 생성 감사로그 기록
- 문항 후보 추가, 문항 선택 저장, 답변 저장 감사로그 기록
- 자소서 분석 실행 결과 감사로그 기록
- 변경 전/후 값을 JSON 문자열로 저장하도록 구현
@whc9999 whc9999 self-assigned this May 22, 2026
@whc9999 whc9999 added the fix label May 22, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

📝 Walkthrough

Walkthrough

This PR introduces a Spring AOP-based audit logging system that annotates write operations across multiple domain services. A new @AuditLogEvent annotation triggers recording of action metadata, user context, before/after payloads, and request details (IP, user-agent) into an AuditLog JPA entity. The feature is applied to analysis, question selection, job posting, and mock application workflows.

Changes

Audit Logging System

Layer / File(s) Summary
Audit event annotation contract
src/main/java/com/jobdri/jobdri_api/domain/audit/annotation/AuditLogEvent.java
@AuditLogEvent annotation defines mandatory action and targetType fields plus optional SpEL-evaluated targetId for marking auditable methods.
Audit log persistence layer
src/main/java/com/jobdri/jobdri_api/domain/audit/entity/AuditLog.java, src/main/java/com/jobdri/jobdri_api/domain/audit/repository/AuditLogRepository.java
AuditLog JPA entity stores user, action metadata, before/after payloads, request context (IP, user-agent), and timestamp; AuditLogRepository provides Spring Data persistence.
Audit event recording service
src/main/java/com/jobdri/jobdri_api/domain/audit/service/AuditLogService.java
AuditLogService extracts HTTP request IP via X-Forwarded-For/X-Real-IP headers and user-agent, serializes payloads to JSON with fallback to string representation, and transactionally persists AuditLog records.
Audit event AOP interception
src/main/java/com/jobdri/jobdri_api/domain/audit/aop/AuditLogAspect.java
AuditLogAspect intercepts @AuditLogEvent annotated methods: extracts method parameters and optional User instance, evaluates SpEL targetId expression with parameter binding and result access, delegates recording to AuditLogService, and safely re-throws audit failures while returning original method result.
Analysis service audit integration
src/main/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisService.java
AnalysisService.analyze() annotated with @AuditLogEvent for ANALYSIS_RUN action on MOCK_APPLY target; optimized response path to return directly-constructed response instead of re-fetching via getAnalysis().
Question service audit integration
src/main/java/com/jobdri/jobdri_api/domain/analysis/service/QuestionService.java
Three transactional question methods annotated with @AuditLogEvent: addCustomQuestionCandidate() (CUSTOM_QUESTION_CANDIDATE_ADD), saveSelectedQuestions() (QUESTION_SELECTION_SAVE), saveAnswers() (QUESTION_ANSWER_SAVE).
JobPosting and MockApply service audit integration
src/main/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingService.java, src/main/java/com/jobdri/jobdri_api/domain/mockapply/service/MockApplyService.java
JobPostingService.createJobPosting() and updateJobPosting() annotated for JOB_POSTING_CREATE/UPDATE; MockApplyService.createActualApply(), createMockApplyFromJobPosting(), and createMockApply() annotated for MOCK_APPLY_CREATE action.

Payment Response JSON Flexibility

Layer / File(s) Summary
Toss payment response JSON handling
src/main/java/com/jobdri/jobdri_api/domain/payment/dto/toss/TossPaymentConfirmResponse.java
TossPaymentConfirmResponse record annotated with @JsonIgnoreProperties(ignoreUnknown = true) to safely deserialize API responses with unknown fields.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • JobDri-Developer/BackEnd#61: Both PRs modify AnalysisService.analyze() method; this PR adds audit logging and optimizes the response path, while the related PR introduces the core analysis workflow.

Poem

🐰 Hop and record, the audit trail grows,
Each write operation now the system knows,
AOP aspects watch with SpEL in hand,
Before and after, a footprint so grand,
From question to payment, the logs now stand.

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'Fix/#62 payment response' is partially related to the changeset. While payment response handling is addressed, the PR primarily adds comprehensive audit logging functionality across multiple services, which is not captured in the title. Update the title to reflect the main change: something like 'Add audit logging functionality and fix payment response parsing' would better represent the scope of changes.
Description check ⚠️ Warning The description follows the template structure and correctly identifies the PR purpose (code improvement and code changes). However, the critical '📋 세부 내용' section is completely empty, leaving the actual work details unexplained. Complete the '📋 세부 내용' section with a detailed explanation of the audit logging implementation and payment response fix.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/#62-payment-response

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/main/java/com/jobdri/jobdri_api/domain/payment/dto/toss/TossPaymentConfirmResponse.java (1)

6-13: Consider validating the payment status in the response.

The status field is captured but not validated in PaymentService.validateTossResponse(). If the Toss API returns a non-successful status (e.g., "FAILED", "CANCELED") with an HTTP 200 response, the current validation would not catch it, potentially completing a payment that actually failed.

Consider adding status validation in PaymentService.validateTossResponse():

private void validateTossResponse(PaymentConfirmRequest request, TossPaymentConfirmResponse response) {
    if (response == null
            || !request.orderId().equals(response.orderId())
            || !request.paymentKey().equals(response.paymentKey())
            || response.totalAmount() != request.amount()
            || !"DONE".equals(response.status())) {  // or check against expected success status
        throw new GeneralException(GeneralErrorCode.PAYMENT_CONFIRM_FAILED, "결제 승인 응답 검증에 실패했습니다.");
    }
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/jobdri/jobdri_api/domain/payment/dto/toss/TossPaymentConfirmResponse.java`
around lines 6 - 13, The validateTossResponse check in
PaymentService.validateTossResponse currently ignores the Toss response status;
update the method to also verify response.status() equals the expected success
value (e.g., "DONE") in addition to existing checks (orderId, paymentKey,
totalAmount) and throw GeneralException(GeneralErrorCode.PAYMENT_CONFIRM_FAILED,
...) if it does not match; reference TossPaymentConfirmResponse.status(),
TossPaymentConfirmResponse.orderId(), TossPaymentConfirmResponse.paymentKey(),
and TossPaymentConfirmResponse.totalAmount() when implementing the validation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/com/jobdri/jobdri_api/domain/audit/aop/AuditLogAspect.java`:
- Around line 50-57: The catch block in AuditLogAspect that currently rethrows
RuntimeException is causing audit failures to bubble up into business logic;
update the exception handling in AuditLogAspect (the catch(RuntimeException e)
around the audit write) to log the error (as it already does) but do not rethrow
— swallow the exception so the joinPoint/proceeding business flow is unaffected;
keep or enhance the log call for visibility (include the exception) and
optionally emit a metric/event for monitoring instead of throwing.

In
`@src/main/java/com/jobdri/jobdri_api/domain/audit/service/AuditLogService.java`:
- Around line 41-43: The ipAddress resolved from headers must be truncated to
the DB column size before persisting: introduce a MAX_IP_LENGTH constant
(matching the VARCHAR(100)) and apply truncation to the output of
resolveIpAddress wherever used (e.g., the call that currently passes
resolveIpAddress(request) into the Audit log constructor and the other
occurrences in the 53-67 block); ensure you truncate (not just validate) to
MAX_IP_LENGTH the returned String before creating the AuditLog/DTO, similar to
how truncate(resolveUserAgent(request), MAX_USER_AGENT_LENGTH) is used.

---

Nitpick comments:
In
`@src/main/java/com/jobdri/jobdri_api/domain/payment/dto/toss/TossPaymentConfirmResponse.java`:
- Around line 6-13: The validateTossResponse check in
PaymentService.validateTossResponse currently ignores the Toss response status;
update the method to also verify response.status() equals the expected success
value (e.g., "DONE") in addition to existing checks (orderId, paymentKey,
totalAmount) and throw GeneralException(GeneralErrorCode.PAYMENT_CONFIRM_FAILED,
...) if it does not match; reference TossPaymentConfirmResponse.status(),
TossPaymentConfirmResponse.orderId(), TossPaymentConfirmResponse.paymentKey(),
and TossPaymentConfirmResponse.totalAmount() when implementing the validation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 34d75390-4102-44c1-a274-131371385507

📥 Commits

Reviewing files that changed from the base of the PR and between adf7cc8 and 2995178.

📒 Files selected for processing (10)
  • src/main/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisService.java
  • src/main/java/com/jobdri/jobdri_api/domain/analysis/service/QuestionService.java
  • src/main/java/com/jobdri/jobdri_api/domain/audit/annotation/AuditLogEvent.java
  • src/main/java/com/jobdri/jobdri_api/domain/audit/aop/AuditLogAspect.java
  • src/main/java/com/jobdri/jobdri_api/domain/audit/entity/AuditLog.java
  • src/main/java/com/jobdri/jobdri_api/domain/audit/repository/AuditLogRepository.java
  • src/main/java/com/jobdri/jobdri_api/domain/audit/service/AuditLogService.java
  • src/main/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingService.java
  • src/main/java/com/jobdri/jobdri_api/domain/mockapply/service/MockApplyService.java
  • src/main/java/com/jobdri/jobdri_api/domain/payment/dto/toss/TossPaymentConfirmResponse.java

Comment on lines +50 to +57
} catch (RuntimeException e) {
log.warn("Audit log recording failed. action={}, method={}",
auditLogEvent.action(),
joinPoint.getSignature().toShortString(),
e
);
throw e;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid propagating audit write failures to business flow.

Rethrowing here means transient audit issues (serialization/DB) can fail user-facing write operations across all annotated services. This should be best-effort logging unless fail-closed is explicitly required.

Suggested fix
         } catch (RuntimeException e) {
             log.warn("Audit log recording failed. action={}, method={}",
                     auditLogEvent.action(),
                     joinPoint.getSignature().toShortString(),
                     e
             );
-            throw e;
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/jobdri/jobdri_api/domain/audit/aop/AuditLogAspect.java`
around lines 50 - 57, The catch block in AuditLogAspect that currently rethrows
RuntimeException is causing audit failures to bubble up into business logic;
update the exception handling in AuditLogAspect (the catch(RuntimeException e)
around the audit write) to log the error (as it already does) but do not rethrow
— swallow the exception so the joinPoint/proceeding business flow is unaffected;
keep or enhance the log call for visibility (include the exception) and
optionally emit a metric/event for monitoring instead of throwing.

Comment on lines +41 to +43
resolveIpAddress(request),
truncate(resolveUserAgent(request), MAX_USER_AGENT_LENGTH)
));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Constrain IP length before persisting audit rows.

ipAddress is stored into a VARCHAR(100) column, but header-derived input is unbounded. A long/spoofed value can trigger a DB length exception and fail the request path.

Suggested fix
 public class AuditLogService {

     private static final int MAX_USER_AGENT_LENGTH = 500;
+    private static final int MAX_IP_ADDRESS_LENGTH = 100;
@@
         auditLogRepository.save(AuditLog.create(
                 user,
                 action,
                 targetType,
                 targetId,
                 toJson(beforeValue),
                 toJson(afterValue),
-                resolveIpAddress(request),
+                truncate(resolveIpAddress(request), MAX_IP_ADDRESS_LENGTH),
                 truncate(resolveUserAgent(request), MAX_USER_AGENT_LENGTH)
         ));
     }

Also applies to: 53-67

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/jobdri/jobdri_api/domain/audit/service/AuditLogService.java`
around lines 41 - 43, The ipAddress resolved from headers must be truncated to
the DB column size before persisting: introduce a MAX_IP_LENGTH constant
(matching the VARCHAR(100)) and apply truncation to the output of
resolveIpAddress wherever used (e.g., the call that currently passes
resolveIpAddress(request) into the Audit log constructor and the other
occurrences in the 53-67 block); ensure you truncate (not just validate) to
MAX_IP_LENGTH the returned String before creating the AuditLog/DTO, similar to
how truncate(resolveUserAgent(request), MAX_USER_AGENT_LENGTH) is used.

@whc9999 whc9999 merged commit 2995178 into main May 22, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant